home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 04 - 1988 / 04.04 Apr 88 / TearOffMDEF Source / TearOffMDEF.c next >
Encoding:
C/C++ Source or Header  |  1988-02-12  |  13.4 KB  |  411 lines  |  [TEXT/KAHL]

  1. /*
  2. ----------------------------------------------------------------------------------------------------
  3. T E A R O F F   M E N U   D E F I N I T I O N
  4.  
  5.     version 1.0
  6.     by Don Melton and Mike Ritter
  7.     
  8.     Copyright (C)1987, 1988 by Impulse Technologies, Inc., all rights reserved. 
  9.     
  10.     Filename:            TearOffMDEF.c
  11.     Font:                    Monaco, 9 point
  12.     Tab setting:    2
  13.     Compiler:            LightspeedC 2.15, Project type: MDEF, ID: 128, Name: (None)
  14.     
  15.     IMPORTANT! Use ResEdit to set the MDEF resource to non-purgeable.
  16.     
  17.     IMPORTANT! Use ResEdit or FEdit to change hex string '21c809ce' at hex offset 14 in the MDEF
  18.     resource to '4e714e71.' This substitutes two 'nop' instructions for a 'move.l a0,ToolScratch.'
  19.  
  20. ----------------------------------------------------------------------------------------------------
  21. DESCRIPTION
  22.  
  23.     TearOffMDEF is an MDEF code resource implementing the common actions of tear-off menus for any
  24.     application. It's designed to communicate with an application via a TearOffMenuGlobals structure
  25.     in a TOMG resource. At runtime, TearOffMDEF uses the menuID of the MenuInfo, passed to it by the
  26.     Macintosh ROM, to find a TOMG resource of the same ID. The TearOffMenuGlobals structure contains
  27.     eight elements:
  28.     
  29.         drawMenuProc        A function pointer to a pascal-type procedure which draws the contents of the
  30.                                         menu in global coordinates within the WMgrPort.
  31.         findItemProc        A function pointer to a pascal-type procedure which returns the number of the
  32.                                         menu item where the mouse is currently located.
  33.         hiliteItemProc    A function pointer to a pascal-type procedure which hilites or unhilites a
  34.                                         given menu item.
  35.         environment            A pointer to a SysEnvRec used to test if the 64K ROMs are present.
  36.         paletteWindow        A WindowPtr used to calculate menuWidth, menuHeight, and the structure boundary
  37.                                         from the window's portRect.
  38.         currentItem            A short int containing the number of the currently hilited menu item.
  39.         position                A Point passed to the application from TearOffMDEF indicating the topLeft point,
  40.                                         in global coordinates, of a torn-off menu.
  41.         itemHilited            A Boolean used internally by TearOffMDEF.
  42.     
  43.     All of these elements, except position and itemHilited, must be initialized by the application
  44.     before a tear-off menu is inserted into the menu list.
  45.     
  46.     IMPORTANT! The menuProc field of a tear-off MENU resource must equal TearOffMDEF's resource ID.
  47.     
  48.     The application is completely responsible for the appearance of the menu. TearOffMDEF does not
  49.     contain code to draw the contents of a menu, nor does it contain code to hilite menu items. A
  50.     set of the three following procedures must be declared in the application for each tear-off menu:
  51.     
  52.         pascal void DrawMenuProc(destRect)
  53.         Rect *destRect;
  54.         
  55.                     destRect    The menu rectangle in the current grafPort.
  56.         
  57.         pascal short FindItemProc(mousePt)
  58.         Point mousePt;
  59.         
  60.                     mousePt        The point in which the mouse is currently located, relative to the topLeft of
  61.                                         the menu rectangle.
  62.                                         
  63.                                         The number of the menu item where the mouse is currently located should be
  64.                                         returned to TearOffMDEF by the application.
  65.         
  66.         pascal void HiliteItemProc(destRect, item, hilite)
  67.         Rect *destRect;
  68.         short item;
  69.         Boolean hilite;
  70.         
  71.                     destRect    The menu rectangle in the current grafPort.
  72.                     item            The number of the menu item to be hilited or unhilited.
  73.                     hilite        A flag indicating the hilite state. Set true to hilite and false to unhilite.
  74.     
  75.     IMPORTANT! The segment in which these procedures are declared must remain locked at runtime.
  76.     
  77.     It's very simple for the application to call these same procedures to draw into the window once
  78.     the user has torn it off.
  79.     
  80.     TearOffMDEF will return -1 to the Menu Manager if a menu has been torn off. It's the
  81.     application's responsibility to move the window into position and make it visible.
  82.     
  83.     Note: TearOffMDEF is not designed to implement pop-up menus. */
  84.  
  85.  
  86.  
  87. /*
  88. ----------------------------------------------------------------------------------------------------
  89. INCLUDE DEFINITIONS */
  90.  
  91. #include "MacTypes.h"
  92. #include "MemoryMgr.h"
  93. #include "MenuMgr.h"
  94. #include "OSUtil.h"
  95. #include "pascal.h"
  96. #include "Quickdraw.h"
  97. #include "ResourceMgr.h"
  98. #include "WindowMgr.h"
  99.  
  100. /*
  101. ----------------------------------------------------------------------------------------------------
  102. GLOBAL CONSTANT DEFINITIONS */
  103.  
  104. #define nil 0
  105.  
  106. typedef struct QuickDrawGlobals { 
  107.     char private[76];
  108.     long randSeed;
  109.     BitMap screenBits;
  110.     Cursor arrow;
  111.     Pattern dkGray;
  112.     Pattern ltGray;
  113.     Pattern gray;
  114.     Pattern black;
  115.     Pattern white;
  116.     GrafPtr thePort;
  117. } QuickDrawGlobals;
  118.  
  119. typedef struct TearOffMenuGlobals {
  120.     void (*drawMenuProc)();
  121.     short (*findItemProc)();
  122.     void (*hiliteItemProc)();
  123.     SysEnvRec *environment;
  124.     WindowPtr paletteWindow;
  125.     Point position;
  126.     short currentItem;
  127.     Boolean itemHilited;
  128. } TearOffMenuGlobals, *TearOffMGlobalsPtr, **TearOffMGlobalsHdl;
  129.  
  130. #define TEAR_OFF_MENU_GLOBALS_TYPE 'TOMG'
  131.  
  132. #define TEAR_OFF_MARGIN 15
  133. #define MOVE_PALETTE_ITEM (-1)
  134.  
  135. #define PALETTE_TITLE_BAR_HEIGHT 10
  136. #define PALETTE_SHADOW_INDENT 3
  137. #define PALETTE_OFFSET 5
  138.  
  139. /*
  140. ----------------------------------------------------------------------------------------------------
  141. GLOBAL VARIABLE DECLARATIONS */
  142.  
  143. /* None, even though the LightspeedC compiler allows it, others don't. */
  144.  
  145. /*
  146. ----------------------------------------------------------------------------------------------------
  147. EXTERNAL FUNCTION DECLARATIONS */
  148.  
  149. /* None, they're not allowed. */
  150.  
  151. /*
  152. ----------------------------------------------------------------------------------------------------
  153. FORWARD FUNCTION DECLARATIONS */
  154.  
  155. pascal void main();
  156. void ChooseItem();
  157. void DragMenu();
  158.  
  159.  
  160.  
  161. /*
  162. ----------------------------------------------------------------------------------------------------
  163. TEAROFF MENU DEFINITION
  164.  
  165.     The main function is called by the Menu Manager in the Macintosh ROM. It's designed to repsond to
  166.     three messages: drawing the menu, choosing an item, and calculating the menu size. It will not
  167.     respond to a pop-up menu request. Before it dispatches to these routines, it first checks to see
  168.     if the TearOffMenuGlobals structure is available on the heap, and locks it down. It does nothing
  169.     if it cannot find this structure.
  170.     
  171.     See 'Inside Macintosh Volume I,' pages 362-363, for more information on this function. */
  172.  
  173. pascal void main(message, whichMenu, menuRect, hitPt, whichItem)
  174. short message;
  175. MenuHandle whichMenu;
  176. Rect *menuRect;
  177. Point hitPt;
  178. short *whichItem;
  179. {
  180.     TearOffMGlobalsHdl whichTOMGlobalsHdl;
  181.     TearOffMGlobalsPtr tearOffMGlobals;
  182.     
  183.     /* Make sure A5 is valid so the application code can use it's own global variables. */
  184.     SetUpA5();
  185.     
  186.     /* Use the menuID to get a handle to the correct TearOffMenuGlobals structure. */
  187.     whichTOMGlobalsHdl = (TearOffMGlobalsHdl) GetResource(TEAR_OFF_MENU_GLOBALS_TYPE,
  188.             (*whichMenu)->menuID);
  189.     
  190.     if((ResErr == noErr) && (whichTOMGlobalsHdl != nil)) {
  191.         /* Lock the TearOffMenuGlobals structure so it can be dereferenced safely. */
  192.         HLock(whichTOMGlobalsHdl);
  193.         tearOffMGlobals = *whichTOMGlobalsHdl;
  194.     
  195.         /* Dispatch according to the message received from the Macintosh ROM.    Use multiple 'if's
  196.         rather than 'switch' to avoid linking of 400+ bytes of runtime code. */
  197.         
  198.         if(message == mDrawMsg) {
  199.             CallPascal(menuRect, (tearOffMGlobals)->drawMenuProc);
  200.             /* Hilite currentItem here instead of in ChooseItem(). It may not be called immediately. */
  201.             CallPascal(menuRect, (tearOffMGlobals)->currentItem, true,
  202.                     (tearOffMGlobals)->hiliteItemProc);
  203.             /* Let ChooseItem() know currentItem has been hilited. */
  204.             (tearOffMGlobals)->itemHilited = true;
  205.         }
  206.         else if(message == mChooseMsg) {
  207.             ChooseItem(tearOffMGlobals, menuRect, hitPt, whichItem);
  208.         }
  209.         else if(message == mSizeMsg) {
  210.             /* Calculate the menuRect from the paletteWindow's portRect. Don't assume that the topLeft of
  211.             the portRect is 0,0. */
  212.             (*whichMenu)->menuWidth
  213.                     = ((tearOffMGlobals)->paletteWindow)->portRect.right
  214.                     - ((tearOffMGlobals)->paletteWindow)->portRect.left;
  215.             (*whichMenu)->menuHeight
  216.                     = ((tearOffMGlobals)->paletteWindow)->portRect.bottom
  217.                     - ((tearOffMGlobals)->paletteWindow)->portRect.top;
  218.         }
  219.         HUnlock(whichTOMGlobalsHdl);
  220.     }
  221.     RestoreA5();
  222. }
  223.  
  224. /*
  225. ----------------------------------------------------------------------------------------------------
  226. CHOOSE ITEM
  227.  
  228.     If the mouse is within the menuRect, ChooseItem() will call the application to find in which item
  229.     the mouse is currently located. The application is also called to hilite or unhilite a single item.
  230.     If the mouse is outside the menuRgn ChooseItem will call DragMenu(). The menuRgn is defined as an
  231.     area made up of the menuRect with a margin and the menu bar. */
  232.  
  233. void ChooseItem(tearOffMGlobals, menuRect, hitPt, whichItem)
  234. TearOffMGlobalsPtr tearOffMGlobals;
  235. Rect *menuRect;
  236. Point hitPt;
  237. short *whichItem;
  238. {
  239.     short saveItem;
  240.     short hiliteItem;
  241.     Point mousePt;
  242.     Rect marginRect;
  243.     RgnHandle menuRgn;
  244.     RgnHandle tempRgn;
  245.     QuickDrawGlobals *qdGlobals;
  246.     
  247.     /* Set up a pointer to QuickDraw's global variables. */
  248.     qdGlobals = (QuickDrawGlobals *)(*(Byte **)CurrentA5
  249.             - (sizeof(QuickDrawGlobals) - sizeof(GrafPtr)));
  250.     
  251.     saveItem = *whichItem;
  252.     hiliteItem = *whichItem = 0;
  253.     
  254.     /* If the hitPt is empty, the Menu Manager is calling TearOffMDEF repeatedly to flash the item. */
  255.     
  256.     if((hitPt.h != 0) && (hitPt.v != 0)) {
  257.     
  258.         if(PtInRect(hitPt, menuRect)) {
  259.             mousePt.h = hitPt.h - menuRect->left;
  260.             mousePt.v = hitPt.v - menuRect->top;
  261.             
  262.             hiliteItem = *whichItem = CallPascalW(mousePt, (tearOffMGlobals)->findItemProc);
  263.             
  264.             if((tearOffMGlobals)->itemHilited) {
  265.                 saveItem = (tearOffMGlobals)->currentItem;
  266.                 (tearOffMGlobals)->itemHilited = false;
  267.             }
  268.         }
  269.         else {
  270.         
  271.             if(!(tearOffMGlobals)->itemHilited) {
  272.                 hiliteItem = (tearOffMGlobals)->currentItem;
  273.                 (tearOffMGlobals)->itemHilited = true;
  274.             }
  275.         }
  276.     }
  277.     if(saveItem != hiliteItem) {
  278.         /* Unhilite the old item. */
  279.         CallPascal(menuRect, saveItem, false, (tearOffMGlobals)->hiliteItemProc);
  280.         /* Hilite the new item. */
  281.         CallPascal(menuRect, hiliteItem, true, (tearOffMGlobals)->hiliteItemProc);
  282.     }
  283.     /* Calculate the menu region. if the mouse is outside this region tear off the menu. */
  284.     marginRect = *menuRect;
  285.     marginRect.left -= TEAR_OFF_MARGIN;
  286.     marginRect.bottom += TEAR_OFF_MARGIN;
  287.     marginRect.right += TEAR_OFF_MARGIN;
  288.     RectRgn(menuRgn = NewRgn(), &marginRect);
  289.     
  290.     RectRgn(tempRgn = NewRgn(), &qdGlobals->screenBits.bounds);
  291.     
  292.     if(tearOffMGlobals->environment->machineType < envMachUnknown) {
  293.         /* Arrggh! Someone has the 64K ROMs. */
  294.         (*tempRgn)->rgnBBox.bottom = 20;
  295.     }
  296.     else {
  297.         (*tempRgn)->rgnBBox.bottom = MBarHeight;
  298.     }
  299.     UnionRgn(menuRgn, tempRgn, menuRgn);
  300.     
  301.     if(!PtInRgn(hitPt, menuRgn)) {
  302.         DragMenu(tearOffMGlobals, hitPt, whichItem, menuRgn, tempRgn, qdGlobals);
  303.     }
  304.     DisposeRgn(tempRgn);
  305.     DisposeRgn(menuRgn);
  306. }
  307.  
  308. /*
  309. ----------------------------------------------------------------------------------------------------
  310. DRAG MENU
  311.  
  312.     DragMenu() draws an outline of the paletteWindow as the mouse is moved outside the menuRgn. If the
  313.     mouse button is released outside this region, DragMenu() will set whichItem to -1 and exit. */
  314.  
  315. void DragMenu(tearOffMGlobals, hitPt, whichItem, menuRgn, saveRgn, qdGlobals)
  316. TearOffMGlobalsPtr tearOffMGlobals;
  317. Point hitPt;
  318. short *whichItem;
  319. RgnHandle menuRgn;
  320. RgnHandle saveRgn;
  321. QuickDrawGlobals *qdGlobals;
  322. {
  323.     PenState savePen;
  324.     long finalTicks;
  325.     Rect windowRect;
  326.     RgnHandle structureRgn;
  327.     short hOffset;
  328.     Point oldMouse;
  329.     Point newMouse;
  330.     RgnHandle dragRgn;
  331.     
  332.     GetPenState(&savePen);
  333.     PenSize(1, 1);
  334.     PenMode(notPatXor);
  335.     PenPat(qdGlobals->gray);
  336.     
  337.     /* Since paletteWindow may not be visible, its portRect must be used to calculate structureRgn. */
  338.     /* First, calculate the window frame. */
  339.     windowRect = (tearOffMGlobals->paletteWindow)->portRect;
  340.     
  341.     /* Correct windowRect if the topLeft of the portRect is not 0,0. */
  342.     OffsetRect(&windowRect, -windowRect.left, -windowRect.top);
  343.     
  344.     windowRect.right += 3;
  345.     windowRect.bottom += PALETTE_TITLE_BAR_HEIGHT + 3;
  346.     RectRgn(structureRgn = NewRgn(), &windowRect);
  347.     
  348.     hOffset = windowRect.right / 2;
  349.     
  350.     /* Next, calculate the frame's drop shadow. */
  351.     windowRect.top += PALETTE_SHADOW_INDENT;
  352.     windowRect.left += PALETTE_SHADOW_INDENT;
  353.     windowRect.bottom += 1;
  354.     windowRect.right += 1;
  355.     RectRgn(saveRgn, &windowRect);
  356.     
  357.     /* Combine the frame and the shadow to calculate the structure. */
  358.     UnionRgn(structureRgn, saveRgn, structureRgn);
  359.     
  360.     GetClip(saveRgn);
  361.     SetClip(GrayRgn);
  362.     
  363.     oldMouse = newMouse = hitPt;
  364.     
  365.     CopyRgn(structureRgn, dragRgn = NewRgn());
  366.     OffsetRgn(dragRgn, newMouse.h - hOffset, newMouse.v - PALETTE_OFFSET);
  367.     
  368.     /* Draw first drag outline. */
  369.     FrameRgn(dragRgn);
  370.     
  371.     /* Mimic DragGrayRgn() by staying in a loop until the mouse button is released or the mouse is
  372.     inside the menuRgn again. */
  373.     
  374.     do {
  375.         /* Don't erase old drag outline if the mouse hasn't moved. */
  376.         
  377.         if(!EqualPt(newMouse, oldMouse)) {
  378.             /* Erase old drag outline. */
  379.             FrameRgn(dragRgn);
  380.             
  381.             CopyRgn(structureRgn, dragRgn);
  382.             OffsetRgn(dragRgn, newMouse.h - hOffset, newMouse.v - PALETTE_OFFSET);
  383.             
  384.             /* Draw new drag outline. */
  385.             FrameRgn(dragRgn);
  386.         }
  387.         /* Keep drag outline from flickering. */
  388.         Delay(2, &finalTicks);
  389.         
  390.         oldMouse = newMouse;
  391.         GetMouse(&newMouse);
  392.     }
  393.     while(WaitMouseUp() && !PtInRgn(newMouse, menuRgn));
  394.     
  395.     /* Erase final drag outline. */
  396.     FrameRgn(dragRgn);
  397.     
  398.     if(!PtInRgn(newMouse, menuRgn)) {
  399.         (tearOffMGlobals)->position.h = (*dragRgn)->rgnBBox.left + 1;
  400.         (tearOffMGlobals)->position.v = (*dragRgn)->rgnBBox.top + 11;
  401.         
  402.         /* Tell application the menu was torn off. */
  403.         *whichItem = MOVE_PALETTE_ITEM;
  404.     }
  405.     DisposeRgn(structureRgn);
  406.     DisposeRgn(dragRgn);
  407.     
  408.     SetClip(saveRgn);
  409.     SetPenState(&savePen);
  410. }
  411.